var fs=require('fs');

/*****************************************************************************/
function compileHeader()
{
	return [
		'data=function(globalspace,cspace) {\n',
		'	var ret=[];\n'
	];
}

/*****************************************************************************/
function compileFooter()
{
	return '	return ret.length==1?ret[0]:ret.join("");\n}\n';
}


/*****************************************************************************/
function escapeText(text)
{
	if (typeof (text)=="number") return text.toString();
	text=text.replace(/\\/g,'\\\\');
	text=text.replace(/"/g,'\\"');
	text=text.replace(/'/g,"\\'");
	text=text.replace(/\r?\n/g,'\\n');
	text=text.replace(/\r/g,'\\n');
	return text;
}

/*****************************************************************************/
function compileText(text)
{
	return 'ret.push("'+escapeText(text)+'");\n';
}

/*****************************************************************************/
function compileModifiers(modifiers)
{
	if (!modifiers.length) return ["",""];
	var head="";
	var tail="";
	var marr=modifiers.split(/:/);
	marr.pop();
	head="";
	marr.forEach(function(x) {
		var p="";
		var arr;
		if (arr=x.match(/^(\w+)\((.*)\)$/)) {
			x=arr[1];
			p=(arr[2].match(/^(?:".*"|\d+)$/))?arr[2]+",":"cspace."+arr[2]+",";
		}
		head+='globalspace.functions.'+x+'(globalspace,'+p;
		tail+=")";
	});
//	modifiers='globalspace.functions.'+marr.join('(globalspace,globalspace.functions.');
//	for (var i=0;i<marr.length;i++) tail+=")";
//	modifiers+='(globalspace,';
	return [head,tail];
}

/*****************************************************************************/
function compileValue(value,modifiers)
{
//	return '	try {ret.push(cspace.'+value+');} catch(){}\n';
	var marr=compileModifiers(modifiers);
//	return 'ret.push('+marr[0]+reparseArgs(value,value)+marr[1]+');\n';
	return 'ret.push('+marr[0]+'cspace.'+value+marr[1]+');\n';
}

/*****************************************************************************/
function compileInclude(value)
{
	return 'ret.push(globalspace.views.process("'+value+'",cspace));\n';
}

/*****************************************************************************/
function compileRecurse(viewname,dst,src,store,tabs)
{
	store.push(src);
	var restore="";
	for (var i=0;i<store.length;i++) {
		restore+=tabs+"	cspace."+store[i]+"=st["+i+"];\n";
	}
	var ret=
		tabs+'{\n'+
		tabs+'	var st=[cspace.'+store.join(",cspace.")+'];\n'+
		tabs+'	cspace.'+dst+'=cspace.'+src+';\n'+
		tabs+'	ret.push(globalspace.views.process("'+viewname+'",cspace));\n'+
		restore+
		tabs+'}\n';
	return ret;
}


/*****************************************************************************/
function reparseArgs(args,fname)
{
	if (!args.length) return "";
	if (args=="true" || args=="false") return args;
	if (args.match(/^[a-zA-Z][a-z_A-Z\.]*$/)) return "cspace."+args;
	if (args.match(/^(\d+|\d+\.\d*|\.\d+|".*?"|'.*?')$/)) return args;
//	args=args.replace(/\[([^\]]+)\]/)
	var arr=args.match(/^\{(.*)\}$/);
	if (!arr) throw new Error("viewcompiler::reparseArgs("+fname+") - Can not parse args, args='"+args+"'\n");
	var tmp;
	var args=","+arr[1];
	tmp="{";
	var i=0;
	while (arr=args.match(/^,\s*(\w+|".*?")\s*:\s*(".*?"|\d+|\d+\.\d*|\w[\w\.]*)\s*/)) {
		args=args.replace(/^,\s*(\w+|".*?")\s*:\s*(".*?"|\d+|\d+\.\d*|\w[\w\.]*)\s*/,"");
		if (i) tmp+=",";
		tmp+=arr[1];
		tmp+=":";
		tmp+=reparseArgs(arr[2],fname);
		i++;
	}
	tmp+="}";
	return tmp;
}

/*****************************************************************************/
function compileFunctionCall(modifiers,fname,args_src)
{
	var args=args_src;
	var marr=compileModifiers(modifiers);
	if (args.length) args=","+reparseArgs(args_src,fname);
	return 'if (globalspace["controllers"]["'+fname+'"]) try{ret.push('+marr[0]+'globalspace["controllers"]["'+fname+'"](globalspace'+args+')'+marr[1]+');} catch(e) {ret.push(e);} else ret.push("[No controller '+fname+']");\n';
}

/*****************************************************************************/
function compileForeach(var1,value)
{
	return 'if (cspace.'+value+') {var '+var1+'A=cspace.'+value+';for (var '+var1+'=0;'+var1+'<'+var1+'A.length;'+var1+'++) {if (cspace.fields) cspace.fields.foreach_i='+var1+';cspace.'+var1+'='+var1+'A['+var1+'];\n';
}

/*****************************************************************************/
function compileFor(var1,start,end)
{
	var rp1=reparseArgs(start);
	var rp2=reparseArgs(end);
	return '{var '+var1+','+var1+'E;if ('+rp1+'<'+rp2+') {'+var1+'='+rp1+';'+var1+'E='+rp2+';} else {'+var1+'E='+rp1+';'+var1+'='+rp2+';} while ('+var1+'<='+var1+'E) {if (cspace.fields) cspace.fields.foreach_i='+var1+';cspace.'+var1+'='+var1+';'+var1+'++;\n';
}


/*****************************************************************************/
function compileIfUnless(type,value)
{
	if (type=="if") {
		return 'if (cspace.'+value+' && cspace.'+value+'!="0") {\n';
	} else {
		return 'if (!cspace.'+value+' || cspace.'+value+'=="0") {\n';
	}
}

/*****************************************************************************/
function compileIfEq(value1,cmprule,value2)
{
	var r='if (cspace.'+value1+cmprule;
	if (value2.match(/^[\d\"]/)) {
		r+=value2;
	} else {
		r+='cspace.'+value2;
	}
	r+=') {\n';
	return r;
}

/*****************************************************************************/
function compileEach(v1,v2)
{
	return 'if (cspace.fields && cspace.fields.foreach_i%'+v1+'=='+v2+') {\n';
}

/*****************************************************************************/
function compileEach2(varname,v1,v2,notlast)
{
	return 'if ('+varname+'%'+v1+'=='+v2+(notlast?' && '+varname+'+1!='+varname+'A.length':'')+') {\n';
}

/*****************************************************************************/
function compileEndBlock(type,value)
{
	if (type=='foreach') return '}}\n';
	if (type=='for') return '}}\n';
	return '}\n';
}

/*****************************************************************************/
function compileI18NBlock(grp,val,easyDefault)
{
	var call='globalspace.functions.L(globalspace,"'+grp+'","'+val+'",'+(easyDefault?1:0)+')';
	return 'ret.push('+call+');\n';
}

/*****************************************************************************/
function compile(tmpl,tmplfile)
{
	var arr;
	var code=compileHeader();
	var insides=[];
	var tabs="\t";
	if (tmpl==null || tmpl==undefined) {
		code.push(compileFooter());
		return code.join("");
	}
	while ((arr=tmpl.match(/^([^{]*(?:\{[^{]+)*)\{\{(.*?)\}\}/))) {
		var old=tmpl.length;
		tmpl=tmpl.replace( /^([^{]*(?:\{[^{]+)*)\{\{(.*?)\}\}/,'');
		if (old==tmpl.length) throw new Error("ERROR!!!!\n");
		if (arr[1].length) {
			code.push(tabs);
			code.push(compileText(arr[1]));
		}
		var expr=arr[2];
//		var expr=arr[2].trim();
//		var expr=arr[2].replace(/^\s+/,"").replace(/\s+$/,"");

		var arr2;
		if (arr2=expr.match(/^!(\w+)$/)) {
			code.push(tabs);
			code.push(compileI18NBlock(tmplfile,arr2[1]));
		
		} else if (arr2=expr.match(/^!!(\w+)$/)) {
			code.push(tabs);
			code.push(compileI18NBlock("generic",arr2[1]));

		} else if (arr2=expr.match(/^!(\w+)\.(\w+)$/)) {
			code.push(tabs);
			code.push(compileI18NBlock(arr2[1],arr2[2]));
		
		} else if (arr2=expr.match(/^!\+(\w+)$/)) {
			code.push(tabs);
			code.push(compileI18NBlock(tmplfile,arr2[1],true));
			
		} else if (arr2=expr.match(/^\/(if|unless|foreach|each|set|ifeq|unlesseq|switch|each|for)$/)) {
			// close
			tabs=tabs.substr(0,tabs.length-1);
			code.push(tabs);
			code.push(compileEndBlock(arr2[1]));

		} else if (arr2=expr.match(/^(if|unless) (\w+(?:\.\w+(?:\(\))?|\[\d+\])*)$/)) {
			// if/unless
			code.push(tabs);
			code.push(compileIfUnless(arr2[1],arr2[2]));
			tabs+="\t";

		} else if (arr2=expr.match(/^if (\w+(?:\.\w+(?:\(\))?)*)(==|!=|<|>|<=|>=)(\d+|".*"|\w+(?:\.\w+(?:\(\))?)*)$/)) {
			// if/unless
			code.push(tabs);
			code.push(compileIfEq(arr2[1],arr2[2],arr2[3]));
			tabs+="\t";

		} else if (arr2=expr.match(/^each (\d+):(\d+)$/)) {
			// each
			code.push(tabs);
			code.push(compileEach(arr2[1],arr2[2]));
			tabs+="\t";

		} else if (arr2=expr.match(/^each (\w+) (\d+):(\d+)$/)) {
			// each
			code.push(tabs);
			code.push(compileEach2(arr2[1],arr2[2],arr2[3],0));
			tabs+="\t";

		} else if (arr2=expr.match(/^eachnotlast (\w+) (\d+):(\d+)$/)) {
			// each
			code.push(tabs);
			code.push(compileEach2(arr2[1],arr2[2],arr2[3],1));
			tabs+="\t";

		} else if (arr2=expr.match(/^foreach (\w+) in (\w+(?:\.\w+(?:\(\))?)*)$/)) {
			// foreach
			code.push(tabs);
			code.push(compileForeach(arr2[1],arr2[2]));
			tabs+="\t";
		
		} else if (arr2=expr.match(/^for (\w+) in (\w+(?:\.\w+(?:\(\))?)*) \.\. (\w+(?:\.\w+(?:\(\))?)*)$/)) {
			// foreach
			code.push(tabs);
			code.push(compileFor(arr2[1],arr2[2],arr2[3]));
			tabs+="\t";

		} else if (arr2=expr.match(/^((?:\w+(?:|\((?:"[^"]*"|\d+|\w+(?:\.\w+)*)\))?:)*)(\w+(?:\.\w+(?:\(\))?|\[\w+(?:\.\w+)*\])*)$/)) {
			// value
			code.push(tabs);
			code.push(compileValue(arr2[2],arr2[1]));
		
		} else if (arr2=expr.match(/^switch \w+(?:\.\w+)*$/)) {
			// switch
			code.push(tabs);
			throw new Error("TODO (switch)");
		
		} else if (arr2=expr.match(/^case "(.*)"$/)) {
			// case
			code.push(tabs);
			throw new Error("TODO (case)");

		} else if (arr2=expr.match(/^((?:\w+(?:|\((?:"[^"]*"|\d+)\))?:)*)(\w+)\((|(?:\w+(?:\.\w+)*|"[^"]*"|\d+)(?:,(?:\w+(?:\.\w+))*|"[^"]*"|\d+)*|\{.*?\})\)$/)) {
			// function call
			code.push(tabs);
			code.push(compileFunctionCall(arr2[1],arr2[2],arr2[3]));

		} else if (arr2=expr.match(/^include "([\w\/\-\_\.]+)"$/)) {
			// include
			code.push(tabs);
			code.push(compileInclude(arr2[1]));

		} else if (arr2=expr.match(/^recurse "([\w\/\-\_\.]+)" (\w+)=([\w\.]+), store=(\w+(?:,\w+)*)$/)) {
			// recurse
			code.push(compileRecurse(arr2[1],arr2[2],arr2[3],arr2[4].split(/,/),tabs));
		} else {
			throw new Error("viewcomplier: Unknown code:\n'"+expr+"'");
		}
	}
	if (tmpl.length) {
		code.push(tabs);
		code.push(compileText(tmpl));
	}
	code.push(compileFooter());
	return code.join("");
}

/*****************************************************************************/
function mkdir_rec(basepath,path,isFile)
{
	var arr=path.split(/\//);
	var max=isFile?arr.length-1:arr.length;
	for (var i=0;i<max;i++) {
		basepath+=arr[i]+"/";
		var d=new fs.Directory(basepath);
		if (!d.isDirectory()) d.create();
	}
}

/*****************************************************************************/
function compileOrLoad(tmpl,cachedir,tmplfile,do_not_eval) {
	var f;
	var code;
	if (tmplfile && cachedir) {
		f=new fs.File(cachedir+tmplfile+'.tmpl.js');
/*		if (f.isFile()) {
			f.open('r');
			code=f.read().toString('utf-8');
			f.close();
		}*/
	}
	if (!code) code=compile(tmpl,tmplfile);
	if (f) {
		try {
			mkdir_rec(cachedir,tmplfile,1);
			f.open('w');
			f.write(code);
			f.close();
		} catch(e) {
		}
	}
	try {
		var data;
		if (do_not_eval) data=code; else eval(code);
		return data;
	} catch (e) {
		throw new Error("COMPILED EVAL FAILED - "+e+"\n,code=\n"+code);
	}
}

/*****************************************************************************/
exports.compile=compileOrLoad;

function JSON_select_recurse(space,param,values,ret,level)
{
	if (!values) return;
	for (var i=0;i<values.length;i++) {
		var value=values[i];
		ret.push({
			id: value.id,
			name: value.name,
			level: value.level
		});
//		if (value.Children) JSON_select_recurse(space,param,value.Children(),ret,level+1);
	}
}

function JSON_gridrower_recurse(space,colnames,values,ret,level)
{
	if (!values) return;
	for (var i=0;i<values.length;i++) {
		var item=values[i];
		var cells=[];
		for (var j=0;j<colnames.length;j++) cells.push(item[colnames[j]]);
		ret.push({rowClass: "item",cells: cells,id: item.id,level: item.level-1});
//		if (item.Children) JSON_gridrower_recurse(space,colnames,item.Children(),ret,level+1);
	}
}

exports.controllers={
	copy_fields: function(space,params,no_tail) {
		var r="";
		var i=0;
		var arr=params.split(/,/);
		for (i=0;i<arr.length;i++) {
			var v=space.fields[arr[i]];
			if (v==null || v==undefined) continue;
			r+=arr[i]+"="+v;
			if (!no_tail || i+1<arr.length) r+="&";
		}
		return r;
	},
	OR: function(space,params) {
		var i=1;
		while ("v"+i in params) {
			if (params["v"+i]) return params["v"+i];
			i++;
		}
		return "";
	}
};

exports.functions={
	JSON:function(space,value) {
		if (value==undefined) return "undefined";
		return JSON.stringify(value).replace(/<\/script>/g,'</scri"+"pt>');
	},
	JSON_select:function(space,param,values) {
		var ret=[];
		if (param) ret.push({id:"",name:""});
		JSON_select_recurse(space,param,values,ret,1);
		return space.functions.JSON(space,ret);
	},
	JSON_gridrower:function(space,param,values) {
		var ret=[];
		JSON_gridrower_recurse(space,param.split(/,/),values,ret,1);
		return space.functions.JSON(space,ret);
	},
	ADD: function(space,param,value) {
		return 1*param+1*value;
	},
	MUL: function(space,param,value) {
		return param*value;
	},
	MUL1: function(space,param,value) {
		return param*(value-1);
	},
	CROP: function (space,size,text) {
		if (text==null || text==undefined) return "";
		if (typeof text != "string") text=text.toString();
		if (text.length>size) return text.substr(0,size-3)+"...";
		return text;
	},
	LI: function(space,value) {
		if (value==null || value==undefined) return "";
		if (typeof value!= "string") value=value.toString();
		value=value.replace(/&/g,'&amp;');
		value=value.replace(/</g,'&lt;');
		value=value.replace(/>/g,'&gt;');
		return "<li>"+value.replace(/\n/g,"</li><li>")+"</li>";
	},
	B: function(space,value) {
		if (value==undefined) return "";
		value=value.replace(/&/g,'&amp;');
		value=value.replace(/</g,'&lt;');
		value=value.replace(/>/g,'&gt;');
		value=value.replace(/\n/gi,'<br/>');
		return value;
	},
	MinusTags: function(space,value) {
		value=value.replace(/&lt;(\/?)([biua]|h\d)&gt;/ig,function(a,p1,p2) { return "<"+p1+p2+">";});
		value=value.replace(/&lt;a\s+href="(\/[^"]*|http:[^"]*)"&gt;/ig,function(a,p1) { return "<a href=\""+p1+"\">";});
		return value;
	},
	Bminus: function(space,value) {
		var r=space.functions.B(space,value);
		return space.functions.MinusTags(space,r);
	},
	P: function(space,value) {
		if (value==undefined) return "";
		value=value.replace(/&/g,'&amp;');
		value=value.replace(/</g,'&lt;');
		value=value.replace(/>/g,'&gt;');
		value=value.replace(/\n/gi,'</p>\n<p>');
		return value;
	},
	Pminus: function(space,value) {
		var r=space.functions.P(space,value);
		return space.functions.MinusTags(space,r);
	},
	T: function(space,value) {
		if (value==undefined) return "";
		value=value.replace(/&/g,'&amp;');
		value=value.replace(/</g,'&lt;');
		value=value.replace(/>/g,'&gt;');
		return value;
	},
	I: function(space,value) {
		if (value==undefined) return "";
		value=value.replace(/&/g,'&amp;');
		value=value.replace(/</g,'&lt;');
		value=value.replace(/>/g,'&gt;');
		value=value.replace(/"/g,'&quot;');
		return value;
	},
	JS: function(space,value) {
		if (value==undefined) return "";
		if (typeof(value)=="string") value=escapeText(value).replace(/(scr)(ipt)/gi,'$1"+"$2');
		return value;
	},
	DumperNoPre: function(space,value) {
		return space.Dumper(value);
	},
	Dumper: function(space,value) {
		return "<pre>"+space.Dumper(value)+"</pre>";
	},
	process: function(space,value) {
		if (value==undefined) return "";
		if (value==null) return "";
		return space.views.apply(value,space);
	},
	CHK: function(space,value) {
		return value?" checked":"";
	},
	O: function(space,selected,vals) {
		if (!vals) {vals=selected;selected=undefined;}
		if (!vals) return "";
		var r="";
		var cache=[];
		for (var i=0;i<vals.length;i++) {
			var val=vals[i];
			r+="<option value='"+escapeText(val.id)+"'";
			if (selected && val.id==selected) r+=" selected";
			r+=">";
			if (val.level) {
				if (!cache[val.level]) {
					var tmp="";
					for (var j=3;j<val.level*3;j++) tmp+=String.fromCharCode(160);
					cache[val.level]=tmp;
				}
				r+=cache[val.level];
			}
			r+=escapeText(val.name);
			r+="</option>";
		}
		return r;
	},
	L: function(space,grp,val,easyDefault) {
		var lang=space.use_language;
		if (!lang && space.site) lang=space.site.language;
		if (!lang) lang=space.system.language;
		if (space.site && space.site.locales && space.site.locales[lang] && space.site.locales[lang][grp] && space.site.locales[lang][grp][val]) {
			return space.site.locales[lang][grp][val];
		}
		if (space.system.locales && space.system.locales[lang] && space.system.locales[lang][grp] && space.system.locales[lang][grp][val]) {
			return space.system.locales[lang][grp][val];
		}
		if (easyDefault==2) return val;
		if (easyDefault==1) return space.functions.L(space,"generic",val,2);
		return "[I18N("+lang+") "+grp+"."+val+"]";
	},
	preview: function(space,param,value) {
		if (value==null || value==undefined) return "";
		var arr=value.match(/^(.*)(\.\w+)$/);
		return arr[1]+"-preview"+param+arr[2];
	},
	replace: function(space,param,value) {
		var re=new RegExp(param.source);
		return value.replace(re,param.destination);
	},
	format: function(space,param,value) {
		if (typeof value=="null" || typeof value=="undefined" || (typeof value=="string" && value=="")) return "";
		var val=""+value;
		var arr;
		if (!(arr=val.match(/^(-?)(\d+)((?:\.\d+)?)$/))) return value;
		var sign=arr[1];
		var intpart=arr[2];
		var floatpart=arr[3];
		arr=param.match(/^(\d+)G(\d+)$/);
		var intsize=1*arr[1];
		var floatsize=1*arr[2];
		while (intpart.length<intsize) intpart="0"+intpart;
		var intpart2="";
		while (intpart.length>3) {
			if (intpart2.length) intpart2=" "+intpart2;
			intpart2=intpart.substr(intpart.length-3,3)+intpart2;
			intpart=intpart.substr(0,intpart.length-3);
		}
		if (intpart2.length) intpart2=" "+intpart2;
		intpart2=intpart+intpart2;
		if (floatsize==0) return sign+intpart2;
		var floatpart2=floatpart.replace(/^\./,"");
		if (floatpart2.length>floatsize) {
			floatpart2=floatpart2.substr(0,floatsize);
		} else {
			while (floatpart2.length<floatsize) floatpart2+="0";
		}
		return sign+intpart2+"."+floatpart2;
	},
	translit: function(space,value) {
		throw new Error("TODO");/*
		my ($tmpl,$space,$query,$name)=@_;
	$name=new Unicode::Transliterate(from=>"Any",to=>"Latin")->process($name);
	Encode::_utf8_on($name);
	$name=~s/[šŝ]/sh/g;
	$name=~tr/â/ja/;
	$name=~tr/č/ch/;
	$name=~tr/Â/JA/;
	$name=~tr/Č/CH/;
	$name=~tr/éèžû/eezu/;
	$name=~tr/ÉÈŽÛ/EEZU/;
	return $name;*/
	}
};

/*****************************************************************************/
